package com.eascs.ant.data.mongo;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.bson.Document;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import com.alibaba.fastjson.JSON;
import com.eascs.ant.data.entity.authority.AccountAuthority;
import com.eascs.ant.data.entity.hr.HrFavorite;
import com.eascs.ant.data.entity.hr.HrMenu;
import com.eascs.ant.data.mongo.entity.MongoPageQuery;
import com.eascs.ant.data.mongo.entity.MongoSetting;
import com.eascs.ant.data.platform.Platform;
import com.eascs.app.dao.JdbcSession;
import com.eascs.app.dao.PageResult;
import com.eascs.app.log.Logger;
import com.eascs.app.log.LoggerFactory;
import com.eascs.app.monitor.status.ServiceStatus;
import com.eascs.app.monitor.status.ServiceStatusMonitor;
import com.eascs.common.assertion.util.AssertErrorUtils;
import com.eascs.common.util.StopWatch;
import com.eascs.common.util.StringUtil;
import com.eascs.site.page.PaginationInfo;
import com.eascs.site.view.button.Button;
import com.eascs.site.view.button.ButtonHelper;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;

public class MongoSession implements InitializingBean, DisposableBean, ServiceStatusMonitor {

	private final Logger logger = LoggerFactory.getLogger(JdbcSession.class);

	private MongoClient mongoClient;
	
	private MongoDatabase mongoDatabase;

	public MongoSession(MongoSetting mongoSetting) {
		StopWatch watch = new StopWatch();
		try {
			String url = null;
			if (StringUtil.hasText(mongoSetting.getUser())) {
				url = StringUtil.format("mongodb://{}:{}@{}:{}/{}", mongoSetting.getUser(), mongoSetting.getPassword(), mongoSetting.getHost(), mongoSetting.getPort(), mongoSetting.getDatabase());
			} else {
				url = StringUtil.format("mongodb://{}:{}/{}", mongoSetting.getHost(), mongoSetting.getPort(), mongoSetting.getDatabase());
			}
			MongoClientURI uri = new MongoClientURI(url);
			this.mongoClient = new MongoClient(uri);
			this.mongoDatabase = mongoClient.getDatabase(mongoSetting.getDatabase());
		} finally {
			logger.debug("MongoSession.init mongoSetting={},elapsedTime={}", mongoSetting.toString(), watch.elapsedTime());
		}
	}

		
	@Override
	public ServiceStatus getServiceStatus() throws Exception {
		this.mongoDatabase.listCollectionNames();
		return ServiceStatus.UP;
	}

	@Override
	public String getServiceName() {
		return this.getClass().getName() + ":" + this.mongoDatabase.getName();
	}

	@Override
	public void destroy() throws Exception {

	}

	@Override
	public void afterPropertiesSet() throws Exception {
		AssertErrorUtils.assertNotNull(this.mongoDatabase, "mongoDatabase is null");
		AssertErrorUtils.assertNotNull(this.mongoDatabase.getName(), "mongoDatabase name is null");
	}
	
	public MongoDatabase getMongoDatabase() {
		return mongoDatabase;
	}


	/**
	 * 根据指定条件查询
	 * @param entityClass
	 * @param filter
	 * @author hzp
	 * @return
	 */
	public Map<String, Object> findOne(Map<String, Object> filter ,String collectionName) {
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectionName);
		BasicDBObject bo = new BasicDBObject();
		for(String key : filter.keySet()){
			if(StringUtil.isNotBlank(key) && filter.get(key) != null){
				if(filter.get(key) instanceof String){
					Pattern patExpAdd = Pattern.compile((String.valueOf(filter.get(key))),Pattern.CASE_INSENSITIVE);
					bo.append(key, patExpAdd);
				}else {
					bo.append(key, filter.get(key));
				}
			}
		}
		Document first = collection.find(bo).first();
		Map<String, Object> data = new HashMap<String, Object>();
		for(String key : first.keySet()){
			if(key.equals("_id"))
				continue;
			data.put(key, first.get(key));
		}
		return data;
	}
	
	/**
	 * 翻页查找monogo document结果
	 * @param sqlPageQuery
	 * @author hzp
	 * @returnPaginationInfo
	 */
	public PaginationInfo findDataArrayWithPage(MongoPageQuery<Object> sqlPageQuery) {
		String collectionName = sqlPageQuery.getCollectionName();
		PageResult<Object[]> pageResult = null;
		AssertErrorUtils.assertNotNull(collectionName, "collectionName is null");
		List<Object[]> datas = new ArrayList<>();
		int pageSize = sqlPageQuery.getPageSize();
		
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectionName);
		
		FindIterable<Document> findIterable = collection.find().limit(pageSize);
		//过滤条件
		if (sqlPageQuery.getParams().size() > 0) {
			findIterable = findIterable.filter(new Document(sqlPageQuery.getParams()));
		}
		//排序条件
		if (sqlPageQuery.getSorts().size() > 0) {
			findIterable = findIterable.sort(new Document(sqlPageQuery.getSorts()));
		}
		
		Document rf = new Document();
		//指定返回字段
		if (sqlPageQuery.getReturnFields() != null){
			for(String returnField : sqlPageQuery.getReturnFields()){
				rf.append(returnField, 1);
			}
		}
		//指定不返回的字段
		if (sqlPageQuery.getNoReturnFields() != null){
			for(String noReturnField : sqlPageQuery.getNoReturnFields()){
				rf.append(noReturnField, 0);
			}
		}
		findIterable.projection(rf);
		
		int dbRowCount = 0;
		// 是否需要走分页
		if (sqlPageQuery.getPageSize() < Integer.MAX_VALUE && sqlPageQuery.getPageSize() > 0) {
			findIterable = findIterable.skip(sqlPageQuery.getPageSize() * sqlPageQuery.getPageIndex()).limit(sqlPageQuery.getPageSize());
		}
		Set<String> titleSet = null;
		for (Document document : findIterable) {
			List<Object> os = new LinkedList<Object>();
			if(titleSet == null)
				titleSet = document.keySet();
			for(String key : document.keySet()){
				os.add(document.get(key));
			}
			datas.add(os.toArray());
		}
		// count
		if (sqlPageQuery.getPageSize() < Integer.MAX_VALUE && sqlPageQuery.getPageSize() > 0) {
			long count = collection.count(new Document(sqlPageQuery.getParams()));
			AssertErrorUtils.assertTrue(count < Long.valueOf(Integer.MAX_VALUE), "count return too big count={}", count);
			dbRowCount = Integer.valueOf(String.valueOf(count));
		} else {
			dbRowCount = datas.size();
		}

		pageResult = new PageResult<>(sqlPageQuery.getPageSize(), sqlPageQuery.getPageIndex(), dbRowCount);
		pageResult.setData(datas);
		Object[] os = titleSet.toArray();
		String[] titles = new String[os.length];
		Button[] buttons = new Button[os.length];
		
		for(int i=0;i<os.length;i++){
			titles[i] = String.valueOf(os[i]);
			if(titles[i].equals("简历链接")){
				buttons[i] = ButtonHelper.createOpenButton("去51job看下", "");
			}else if(titles[i].equals("ID")){
				buttons[i] = ButtonHelper.createOpenButton("详情", "/hr/queryById?id={"+i+"}");
			}
		}
		PaginationInfo paginationInfo = new PaginationInfo(pageResult, titles);
		paginationInfo.setTitleButtons(buttons);
		return paginationInfo;
	}
	
	/**
	 * 根据指定条件查询
	 * @param entityClass
	 * @param filter
	 * @author hzp
	 * @return
	 */
	public List<Map<String, Object>> findAll(Map<String, String> filter ,String collectionName) {
		List<Map<String, Object>> list = new ArrayList<>();
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectionName);
		BasicDBObject bo = new BasicDBObject();
		for(String key : filter.keySet()){
			if(StringUtil.isNotBlank(key) && StringUtil.isNotBlank(filter.get(key))){
				Pattern patExpAdd = Pattern.compile(filter.get(key),Pattern.CASE_INSENSITIVE);
				bo.append(key, patExpAdd);
			}
		}
		FindIterable<Document> findIterable = collection.find(bo);
		MongoCursor<Document> mongoCursor = findIterable.iterator();
		while(mongoCursor.hasNext()){
			Map<String, Object> data = new HashMap<>();
			Document doc = mongoCursor.next();
			for(String key : doc.keySet()){
				if(key.equals("_id"))
					continue;
				data.put(key, doc.get(key));
			}
			list.add(data);
		}
		return list;
	}
	
	
	/**
	 * 插入数据
	 * @param collectionName
	 * @param data
	 */
	public void set(String collectionName ,Map<String, Object> data){
		MongoCollection<Document> mongoCollection = mongoDatabase.getCollection(collectionName);
		Document document = new Document(data);
		mongoCollection.insertOne(document);
	}
	
	
	/**
	 * 插入一个新的收藏夹或者覆盖写
	 * @param favorite
	 * @return
	 */
	public HrFavorite insertOrUpdateFavorite(HrFavorite favorite){
		MongoCollection<Document> collection = mongoDatabase.getCollection("hr_favorite");
		Document doc = new Document();
		doc.put("account", favorite.getAccount());
		Document qr = collection.find(new Document("account", favorite.getAccount())).first();
		if(qr != null){
			for(String key : qr.keySet()){
				if(!"account".equals(key) && !key.equals("_id"))
					doc.put(key, qr.getString(key));
			}
		}
		for(HrMenu menu : favorite.getMenuList()){
			doc.put(menu.getId(), JSON.toJSONString(menu));
		}
		if(qr != null){
			collection.updateOne(Filters.eq("account", favorite.getAccount()), new Document("$set",doc));
		}else{
			collection.insertOne(doc);
		}
		
		//返回现在的收藏夹
		
		return mergeDoc(doc);
	}
	
	/**
	 * 删除收藏夹中的某份简历
	 * @param account
	 * @param id 简历id
	 */
	public void delFavorite(String account , String id){
		MongoCollection<Document> collection = mongoDatabase.getCollection("hr_favorite");
		Document qr = collection.find(new Document("account", account)).first();
		if(qr != null){
			if(qr.containsKey(id)){
				collection.deleteOne(qr);
				qr.remove(id);
				collection.insertOne(qr);
			}
		}
	}
	
	private static HrFavorite mergeDoc(Document doc){
		HrFavorite fr = new HrFavorite();
		fr.setAccount(doc.getString("account"));
		List<HrMenu> lm = new LinkedList<>();
		for(String key : doc.keySet()){
			if(!key.equals("account") && !key.equals("_id")){
				HrMenu menu = JSON.parseObject(doc.getString(key), HrMenu.class);
				lm.add(menu);
			}
		}
		fr.setMenuList(lm);		
		return fr;
	}
	
	/**
	 * 根据过滤规则来获取数据,依然按时间倒序排序
	 * @param collectName
	 * @param filterMap
	 * @param time  该集合的排序依据的时间，信息每天更新的以官网时间字段信息为准，否则以爬取时间为准
	 * @return
	 */
	public List<Document> getData(String collectName , Map<String, Object> filterMap){
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectName);
		BasicDBObject filter= new BasicDBObject();
		for(String key : filterMap.keySet()){
			if(filterMap.get(key) instanceof Date){
				Date d = (Date)filterMap.get(key);
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				String date = sdf.format(d);
				int startYear = Integer.valueOf(date.split("-")[0]), endYear = Integer.valueOf(date.split("-")[0]);  
				int startMonth = Integer.valueOf(date.split("-")[1]), endMonth = Integer.valueOf(date.split("-")[1]);  
				int startDay = Integer.valueOf(date.split("-")[2]), endDay = Integer.valueOf(date.split("-")[2])+1; 
				
				filter.append(key, new BasicDBObject("$gte", new Date(startYear - 1900, startMonth - 1, startDay)).append("$lt", new Date(endYear - 1900, endMonth -1, endDay)));
			}else
				filter.append(key, filterMap.get(key));
		}
		
		FindIterable<Document> ft = collection
									.find(filter);
		
		MongoCursor<Document> mc = ft.iterator();
		List<Document> list = new LinkedList<Document>();
		while(mc.hasNext()){
			Document doc = mc.next();
			doc.put("platform", Platform.getName(collectName));
			list.add(doc);
		}
		return list;
	}	
	
	/**
	 * 查询指定集合固定条数据,排序字段为降序
	 * @param collectName
	 * @param limit
	 * @param descendFields为降序,ascend为升序
	 * @return
	 */
	public List<Document> getData(String collectName ,int limit ,String[] descendFields){
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectName);
		FindIterable<Document> ft = collection
									.find()
									.limit(limit)
									.sort(Sorts.descending(descendFields));
		MongoCursor<Document> mc = ft.iterator();
		List<Document> list = new LinkedList<Document>();
		while(mc.hasNext()){
			Document doc = mc.next();
			doc.put("platform", Platform.getName(collectName));
			list.add(doc);
		}
		return list;
	}
	
	/**
	 * 从指定mongo集合检索出固定数量的结果集,按时间倒序查询,一般用于默认查询显示
	 * @param collectName
	 * @param 
	 * @param limit
	 * @return
	 */
	public List<Document> getData(String collectName ,String time ,int limit){
		MongoCollection<Document> collection = mongoDatabase.getCollection(collectName);
		FindIterable<Document> ft = collection
									.find()
									.limit(limit);
		MongoCursor<Document> mc = ft.iterator();
		List<Document> list = new LinkedList<Document>();
		while(mc.hasNext()){
			Document doc = mc.next();
			doc.put("platform", Platform.getName(collectName));
			list.add(doc);
		}
		return list;
	}
	
	/**
	 * 根据不同平台将数据data存入不同集合
	 * @param data
	 * @param platform
	 */
	public void setYuqing(Map<String, Object> data , Platform platform){
		set(platform.getPlatform(), data);
	}
	
	public AccountAuthority getAuthority(String account){
		MongoCollection<Document> collection = mongoDatabase.getCollection("authority");
		Document doc = collection.find(new Document("account", account)).first();
		AccountAuthority aa = new AccountAuthority();
		if(doc != null){
			if(doc.containsKey("hr"))
				aa.setHr(doc.getBoolean("hr"));
			if(doc.containsKey("news"))
				aa.setNews(doc.getBoolean("news"));
			if(doc.containsKey("price"))
				aa.setPrice(doc.getBoolean("price"));
		}
		return aa;
	}
	
}
